package com.gitee.wsl.time.schedule.interval

import com.gitee.wsl.time.schedule.AbstractTimeScheduler
import kotlin.time.Instant
import kotlinx.datetime.TimeZone
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

class Interval : AbstractTimeScheduler {

    private val duration: Duration

    constructor(duration: Duration) {
        this.duration = duration
    }

    constructor(durationExpression: String) {
        this.duration = Duration.parse(durationExpression)
    }

    @OptIn(ExperimentalTime::class)
    override fun next(
        createdAtInstant: Instant,
        currentInstant: Instant,
        zoneId: TimeZone
    ): Result<Instant> {
        val durationUntilNow: Duration = createdAtInstant-currentInstant
        val amountOfDurationsUntilNow: Long = durationUntilNow.inWholeNanoseconds / duration.inWholeNanoseconds
        return Result.success(createdAtInstant + duration * (amountOfDurationsUntilNow.toInt() + 1))
    }

    override fun validateSchedule() {
        if (duration.inWholeSeconds < SMALLEST_SCHEDULE_IN_SECONDS) {
            throw IllegalArgumentException(
                "The smallest interval for recurring jobs is $SMALLEST_SCHEDULE_IN_SECONDS seconds. Please also make sure that your 'pollIntervalInSeconds' configuration matches the smallest recurring job interval.",
            )
        }
    }

    override fun toString(): String {
        return duration.toString()
    }

    /**
     * Compares this object against the specified object. The result is `true`
     * if and only if the argument is not `null` and is a `Schedule`
     * object that whose seconds, minutes, hours, days, months, and days of
     * weeks sets are equal to those of this schedule.
     *
     *
     * The expression string used to create the schedule is not considered, as two
     * different expressions may produce same schedules.
     *
     * @param other the object to compare with
     * @return `true` if the objects are the same; `false` otherwise
     */
    override fun equals(other: Any?): Boolean {
        if (other !is Interval) return false
        if (this === other) return true

        return this.duration == other.duration
    }

    override fun hashCode(): Int {
        return duration.hashCode()
    }
}
